package com.pactera.jep.service.sys.service.impl;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.pactera.jep.core.exception.ServiceException;
import com.pactera.jep.orm.Filter;
import com.pactera.jep.orm.mybatis.mapper.Mapper;
import com.pactera.jep.orm.mybatis.service.MyBatisCRUDService;
import com.pactera.jep.service.sys.service.FTPService;
import com.pactera.jep.service.sys.service.FtpAttachmentService;
import com.pactera.jep.sys.mapper.AttachmentMapper;
import com.pactera.jep.sys.model.Attachment;
import com.pactera.jep.sys.model.AttachmentType;
import com.pactera.jep.sys.service.AttachmentService;
import com.pactera.jep.sys.service.AttachmentTypeService;
import com.pactera.jep.sys.service.PKeyService;
import com.pactera.jep.sys.service.StaffService;
import com.pactera.jep.sys.service.impl.AttachmentServiceImpl;

/**
 * {@link AttachmentService}实现类，提供文件与ftp进行交互的功能
 * 
 * @author ghost
 *
 */
@Service("ftpAttachmentService")
@ConditionalOnProperty(name="save.type", havingValue="FTP", matchIfMissing=false)
public class FtpAttachmentServiceImpl extends MyBatisCRUDService<Attachment, String> implements FtpAttachmentService {

	private final static Logger logger = LoggerFactory.getLogger(AttachmentServiceImpl.class);

	@Autowired
	private AttachmentMapper attachmentMapper;

	@Autowired
	private AttachmentTypeService attachmentTypeService;

	@Autowired
	private PKeyService pKeyService;

	@Autowired
	private StaffService staffService;
	
	@Autowired
	private FTPService ftpService;

	@Override
	public Mapper<Attachment, String> getMapper() {
		return attachmentMapper;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public int deleteById(String id) throws ServiceException {
		// 首先根据id从表中获取到附件数据
		Attachment attachment = this.get(id);
		deleteAttachment(attachment);
		return 1;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public int deleteByEntityId(String entityId) throws ServiceException {
		List<Attachment> list = this.select(new Filter.Builder().eq("entityId", entityId).build());
		int count = 0;
		for (Attachment attachment : list) {
			deleteAttachment(attachment);
			count++;
		}
		return count;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public int deleteByUploadSession(String uploadSession) throws ServiceException {
		List<Attachment> list = this.select(new Filter.Builder().eq("uploadSession", uploadSession).build());
		int count = 0;
		for (Attachment attachment : list) {
			deleteAttachment(attachment);
			count++;
		}
		return count;
	}

	/**
	 * 删除单个附件及文件
	 * 
	 * @param attachment
	 * @throws ServiceException
	 */
	@Transactional
	private void deleteAttachment(Attachment attachment) throws ServiceException {
		try {
			if (attachment != null) {
//				AttachmentType type = attachmentTypeService.get(attachment.getAttachTypeCode());
				// 首先获取远程文件名，远程文件名中包含有文件格式
				String remoteName = attachment.getSaveFileName();
				// 然后获取远程目录
				String remotePath = attachment.getUploadPath();
				// 接着通过FTPService去删除FTP上的文件
				boolean result = ftpService.remove(remotePath, remoteName);
				if (!result) {
					throw new ServiceException("文件删除失败!");
				} else {
					this.deleteByPrimaryKey(attachment.getId());
				}
				this.deleteByPrimaryKey(attachment.getId());
			}
		} catch (Exception ex) {
			logger.error("删除失败：" + ex.getMessage());
			throw new ServiceException("删除附件失败", ex);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public List<Attachment> upload(String staffCode, String entityId, String attachTypeCode, String uploadSession,
			MultipartFile file) throws ServiceException {

		List<Attachment> list = new ArrayList<Attachment>();

		String typePath = "";
		AttachmentType attachmentType = null;
		attachmentType = attachmentTypeService.get(attachTypeCode);
		typePath = attachmentType.getAttachPath() + "/";
		// 首先获取上传路径
		String uploadPath = typePath;

		String fileName = null;

		String saveFileName = null;
		boolean result = false;
		try {
			fileName = file.getOriginalFilename();
			if (!StringUtils.isBlank(fileName)) {
				// 首先获取到上传的文件名
				int pos = fileName.lastIndexOf(".");
				String srcFileName = fileName.substring(0, pos);
				String format = fileName.substring(pos + 1);
				// 远程文件名
				saveFileName = pKeyService.getUUIDKey() + "." +format;
				// 以InputStream的方式获取上传文件
				InputStream in = file.getInputStream();
				result = ftpService.save(uploadPath, saveFileName, in);
				if (result) {
					logger.info("文件{}上传成功！", fileName);
					// 上传FTP成功，那么构建Attachment对象并保存该对象
					Attachment attachment = new Attachment();
					attachment.setFileDesc(srcFileName);
					attachment.setAttachTypeCode(attachTypeCode);
					attachment.setFileSize(file.getSize());
					attachment.setSrcFileName(srcFileName);
					attachment.setEntityId(entityId);
					attachment.setFileFormat(format);
					attachment.setSaveFileName(saveFileName);
					attachment.setUploadSession(uploadSession);
					attachment.setUploadTime(new Date());
					attachment.setUploadStaffId(staffCode);
					attachment.setUploadPath(uploadPath);
					attachment.setUploadStaffName(staffService.getNameByCode(staffCode));
					this.insert(attachment);
					list.add(attachment);
				} else {
					throw new ServiceException("文件" + fileName + "上传失败！");
				}
			}
		} catch (Exception e) {
			// 如果在保存数据至mysql的时候出现异常，那么还要根据是否上传成功来决定是否需要删除文件
			if (result) {
				try {
					ftpService.remove(uploadPath, saveFileName);
				} catch (Exception ex) {
					// 删除出现的异常全部忽略
					logger.error("删除已上传至{}的文件{}出现异常：{}", uploadPath, saveFileName, e.getMessage(), e);
				}
			}
			logger.error("文件 {}上传出现异常：{}", fileName, e.getMessage(), e);
			throw new ServiceException("文件" + fileName + "上传出现异常：" + e.getMessage(), e.getCause());
		}

		return list;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<Attachment> select(String entityId, String uploadSession, String entityType) throws ServiceException {
		Map<String, Object> params = new HashMap<>();
		params.put("entityId", entityId);
		params.put("uploadSession", uploadSession);
		params.put("entityType", entityType);
		return attachmentMapper.selectByEntityIdOrUploadSession(params);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@Transactional
	public int associate(String entityId, String uploadSession) throws ServiceException {
		Attachment t = new Attachment();
		t.setEntityId(entityId);
		return this.update(t, new String[] { "entityId" },
				new Filter.Builder().eq("uploadSession", uploadSession).build());
	}

	@Override
	@Transactional
	public List<Attachment> copy(String entityId, String entityType) throws ServiceException {

		List<Attachment> result = new ArrayList<>();
		String newEntityId = "_tmp_" + pKeyService.getUUIDKey();
		String uploadSession = pKeyService.getUUIDKey();
		List<Attachment> list = this
				.select(new Filter.Builder().eq("entityId", entityId).eq("attachTypeCode", entityType).build());
		for (Attachment attachment : list) {
			
			// 获取上传路径
			String uploadPath = attachment.getUploadPath();
			// 获取上传文件名
			String saveFileName = attachment.getSaveFileName();
			// 获取新的文件名
			String saveFileNameNew = pKeyService.getUUIDKey() + "." + attachment.getFileFormat();
			// 然后将文件进行复制
			ftpService.copy(uploadPath, saveFileName, saveFileNameNew);
			
			// 构建附件对象
			Attachment attachmentNew = new Attachment();
			BeanUtils.copyProperties(attachment, attachmentNew);
			attachmentNew.setId(null);
			attachmentNew.setSaveFileName(saveFileNameNew);
			attachmentNew.setEntityId(newEntityId);
			attachmentNew.setUploadSession(uploadSession);
			this.insert(attachmentNew);
			result.add(attachmentNew);

		}
		return result;
	}

}
